package com.itbupt.service.impl;

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itbupt.dto.Result;
import com.itbupt.entity.Shop;
import com.itbupt.mapper.ShopMapper;
import com.itbupt.service.IShopService;
import com.itbupt.utils.CacheClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static com.itbupt.utils.RedisConstants.*;
import com.itbupt.utils.RedisData;

/**
 * @Author BUPT-Dingchang
 * @Create 2024/5/2 17:11
 * ClassName: ShopServiceImpl
 * Description:
 */

@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private CacheClient cacheClient;


    // 根据id查询商户
    //@Override
    //public Result queryById(Long id) {
    //
    //    String key = CACHE_SHOP_KEY + id;
    //    // 在redis中查询
    //    String shopJson = stringRedisTemplate.opsForValue().get(key);
    //    // 存在则返回
    //    if(StrUtil.isNotBlank(shopJson)){
    //        Shop shop = JSONUtil.toBean(shopJson, Shop.class);
    //        return Result.ok(shop);
    //    }
    //    // 如果redis中为""空字符串，返回失败
    //    if(shopJson != null) {
    //        return Result.fail("商户不存在");
    //    }
    //
    //    // 不存在则查询数据库
    //    Shop shop = this.getById(id);
    //    // 数据库中不存在，则返回错误信息
    //    if(shop == null){
    //        // 将空值写入redis---防止缓存穿透
    //        stringRedisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);
    //        return Result.fail("商铺不存在");
    //    }
    //    // 存在的话，存入redis
    //    stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.SECONDS);
    //    return Result.ok(shop);
    //}


    // 根据id更新商户
    @Override
    public Result update(Shop shop) {
        Long id = shop.getId();
        if(id == null) {
            return Result.fail("商铺id不能为空");

        }
        // 更新数据库
        updateById(shop);
        // 删除缓存
        stringRedisTemplate.delete(CACHE_SHOP_KEY + id);
        return Result.ok();
    }

    @Override
    public Result queryShopByType(Integer typeId, Integer current, Double x, Double y) {
        return null;
    }



    // 使用锁来解决缓存击穿问题
    @Override
    public Result queryById(Long id) {
        //// 缓存穿透
        //// Shop shop = queryWithPassThrough(id);
        //// 互斥锁解决缓存击穿
        //Shop shop = queryWithMutex(id);

        // 解决缓存穿透
        Shop shop = cacheClient.queryWithPassThrough(
                CACHE_SHOP_KEY,
                id,
                Shop.class,
                this::getById,
                CACHE_SHOP_TTL,
                TimeUnit.MINUTES);

        if (shop == null) {
            return Result.fail("商户不存在");
        }
        // 返回信息
        return Result.ok(shop);

    }

    //public Shop queryWithMutex(Long id) {
    //    String key = CACHE_SHOP_KEY + id;
    //    // 1、从redis中查询商铺缓存
    //    String shopJson = stringRedisTemplate.opsForValue().get(key);
    //    // 存在则返回
    //    if(StrUtil.isNotBlank(shopJson)){
    //        Shop shop = JSONUtil.toBean(shopJson, Shop.class);
    //        return shop;
    //    }
    //    // 判断命中的值是不是空值
    //    if(shopJson != null) {
    //        return null;
    //    }
    //
    //    // 实现缓存重构
    //    // 获取互斥锁
    //    String lockKey = "lock:shop:" + id;
    //    Shop shop = null;
    //    try {
    //        boolean isLock = tryLock(lockKey);
    //        // 判断是否获取成功
    //        if(!isLock) {
    //            // 未获取到锁，等待重试
    //            Thread.sleep(50);
    //            return queryWithMutex(id);
    //        }
    //        // 成功--查询数据库
    //        shop = this.getById(id);
    //        // 数据库中不存在，则返回错误信息
    //        if(shop == null) {
    //            stringRedisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);
    //            return null;
    //        }
    //
    //        // 存在的话，存入redis
    //        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.SECONDS);
    //    } catch (Exception e) {
    //        throw new RuntimeException(e);
    //    } finally {
    //        // 释放锁
    //        unlock(lockKey);
    //    }
    //
    //    return shop;
    //}



    //// 获取锁
    //private boolean tryLock(String key) {
    //    Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
    //    return BooleanUtil.isTrue(flag);
    //}
    //// 释放锁
    //private void unlock(String key) {
    //    stringRedisTemplate.delete(key);
    //}
    //
    //// 处理缓存穿透
    //public Shop queryWithPassThrough(Long id){
    //    String key = CACHE_SHOP_KEY + id;
    //    //在redis中查询商户
    //    String shopJson = stringRedisTemplate.opsForValue().get(key);
    //    //存在，则返回
    //    if (StrUtil.isNotBlank(shopJson)){
    //        Shop shop = JSONUtil.toBean(shopJson, Shop.class);
    //        return shop;
    //    }
    //    //如果redis中为""空字符串,返回失败
    //    if (shopJson != null){
    //        return null;
    //    }
    //    //不存在,在mysql数据库中查询
    //    Shop shop = getById(id);
    //    //不存在，返回错误信息
    //    if (shop == null){
    //        //把空值写到redis，防止缓存穿透
    //        stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
    //        return null;
    //    }
    //    //存在，将查询的数据放到redis中
    //    stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL,TimeUnit.MINUTES);
    //
    //    //返回信息
    //    return shop;
    //}
    //
    //// 使用逻辑过期方式解决缓存击穿问题
    ////将数据库查询到的shop放到redisData中并放到redis
    //public void saveShopToRedis(Long id,Long expireSeconds){
    //    //数据库中查询商户
    //    Shop shop = getById(id);
    //    //将商户信息和逻辑过期时间封装
    //    RedisData redisData = new RedisData();
    //    redisData.setData(shop);
    //    redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));
    //    //写到redis
    //    stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(redisData));
    //}
    //
    ////线程池
    //private static final ExecutorService CACHE_REBUILD_EXECUTOR= Executors.newFixedThreadPool(10);
    //
    //// 使用逻辑过期方式解决缓存击穿问题
    //public Shop queryWithLogicalExpire(Long id) {
    //    String key = CACHE_SHOP_KEY + id;
    //    // 在redis中查询商户
    //    String redisDataJson = stringRedisTemplate.opsForValue().get(key);
    //    // 未命中则返回null
    //    if (StrUtil.isBlank(redisDataJson)){
    //        return null;
    //    }
    //    // 命中,将json反序列为对象
    //    RedisData redisData = JSONUtil.toBean(redisDataJson, RedisData.class);
    //    JSONObject data = (JSONObject) redisData.getData();
    //    Shop shop = JSONUtil.toBean(data, Shop.class);
    //    // 判断缓存逻辑是否过期
    //    LocalDateTime expireTime = redisData.getExpireTime();
    //    // 未过期,直接返回shop
    //    if (expireTime.isAfter(LocalDateTime.now())) {
    //        return shop;
    //    }
    //    // 已过期，缓存重建
    //    String lockKey = LOCK_SHOP_KEY+id;
    //    // 获取锁
    //    boolean isLock = trylock(lockKey);
    //    // 获取到锁，开启独立线程，实现缓存重建
    //    if (isLock) {
    //        CACHE_REBUILD_EXECUTOR.submit(()->{
    //            try {
    //                saveShopToRedis(id,20L);
    //            } catch (Exception e) {
    //                throw new RuntimeException(e);
    //            }finally {
    //                //释放锁
    //                unlock(lockKey);
    //            }
    //        });
    //    }
    //
    //    //返回信息
    //    return shop;
    //}

}
